/*
 * graph.h contiene le strutture necessarie al concetto di grafo.
	Inoltre, alla stregua di problem.h, contiene anche i metodi concernenti il grafo in sè.
 */

#ifndef GRAPH_H_
#define GRAPH_H_

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "edgeID.h"
#include "edgeGraph.h"

/**indica che si vuole utilizzare un valutatore di tipo PURE
 *
 */
#define EVAL_PURE 0
/**indica che si vuole utilizzare un valutatore di tipo SMART
 *
 */
#define EVAL_SMART 1
/**indica che si vuole utilizzare un valutatore di tipo WISE
 *
 */
#define EVAL_WISE 2
//TODO documentazion
#define EVAL_FAILED -1

/**qualora si debba valutare il peso di un arco con t o d nullo, questo numero e' il valore che assumere il valutatore, ossia
 * il numero che viene considerato peso infinito
 *
 */
//PAT non la sta usando e tutto funziona bene lo stesso. valutare di cancellare la macro
#define INFINITE_WEIGHT __FLT_MAX__*0.000001 //TODO WATCHTHIS pat ha cambiato FLT_MAX > __FLT_MAX__ perchè non andava un cazzus


// ******************* STRUCTS ******************

/** Peso inteso come output di un valutatore. */
typedef float Weight;
/** types di comodo per rendere il più chiaro possibile il codice*/
typedef Weight* Pweight;
typedef Weight** WeightMat;

/** Struttura incapsulante l'intero grafo, caratterizzato anche da due scalari. */
typedef struct Graph {
	AdjMat am;
	/** indica un puntatore ad una variaible che contiene il numero di nodi del grafo. Si e' utilizzato un puntatore
	 * invece di un "int" e basta per evitare ridondanze con altre strutture dati quali TabooList*/
	int* N;
	/** Numero di edges, calcolato inizialmente come N(N-1)/2 e poi salvato per velocità.*/
	int E;
	/**rappresenta il tempo massimo che ogni percorso di ogni camion non può superare*/
	int T;
	/** rappresenta la capacità di ogni camion*/
	int Q;
	/**indica la matrice dei pesi dell'arco secondo il punto di vista del tempo. Materialmente questa matrice incapsula, dato un arco (i;j)
	 * il valore: p_ij/t_ij.
	 */
	WeightMat timeWM;
	/**indica la matrice dei pesi dell'arco secondo il punto di vista della capacita'. Materialmente questa matrice incapsula, dato un arco (i;j)
	 * il valore: p_ij/d_ij.
	 */
	WeightMat loadWM;
	/**indica qual'e, tra gli N nodi, il nodo deposito. E' un numero compreso tra 0 (incluso) e N (incluso).*/
	int depot;
} Graph;
typedef Graph* Pgraph;

// ******************** COSTRUTTORI ***************************


/**costruisce un nuovo grafo vuoto. La matrice costruita sulla base di N viene
 * allocata in memoria ma i valori contenuti NON sono inizializzati. La variabile
 * E della struttura Graph viene invece inizializzata a 0.
 * @param N indica un puntatore ad una variabile indicante il numero di nodi contenuti nel grafo
 * @param T indica il tempo massimo di ciascun camion
 * @param Q indica la capacità massima di ciascun camion
 * @param depot indica il nodo deposito da inserire. Solitamente e' lo zero
 */
Pgraph initGraph(int* N, float T, float Q,int depot);

/**costruisce un nuovo grafo vuoto. La matrice NxN viene allocata in memoria e tutti
 * i valori vengono inizializzati all'edge passato come parametro. Il campo E viene
 * comunque inizializzato a 0.
 *
 * \pre
 *  \li N non e' NULL
 *  \li edge ha i campi settati correttamente
 * \post
 *  \li un grafo inizializzato correttamente
 *  \li il grafo ha dei triangoli che non soddisfano la disuguaglianza triangolare
 *  \li il grafo non e' completo
 *
 *
 * @param N indica un puntatore ad una variabile indicante il numero di nodi contenuti nel grafo
 * @param T indica il tempo massimo di ciascun camion
 * @param Q indica la capacità massima di ciascun camion
 * @param depot indica il numero del deposito di questo grafo
 * @param edge indica l'edge da inserire in tutti gli slot della matrice di adiacenza
 */
Pgraph initCompleteGraph(int* N, float T, float Q,int depot, EdgeGraph edge);

/**inizializza i pesi statici, sia temporali che relativi alla capacita', del grafo.
 * \pre
 *  \li pg non deve essere NULL;
 *  \li pg deve esere gia' stato inizializzato tramite i costruttori di questo header;
 *  \li deve essere gia' stata richiamata la funzione dijkstraFiller();
 * \post
 *  \li all'interno delle matrici timeWM e di loadWM sono espressi i pesi in capacita' e tempo degli archi
 *
 * @param pg il grafo su cui computare i pesi
 */
void initSmartWeights(Pgraph pg);

// ************************* FUNZIONI *******************************

/**stampa l'intero grafo.
 * \pre
 * 	\li f non deve essere NULL
 * 	\li pg non deve essere NULL
 * 	\li pg deve essere stato precedentemente inizializzato tramite la funzione initGraph o initCompleteGraph
 *\post
 *	\li nel file e' presente una rappresentazione del grafo leggibile
 * @param f il file su cui stampare il grafo
 * @param pg il grafo da stampare
 */
void printGraph(FILE* f, Pgraph pg);

/** stampa su file della matrice di adiacenza.
 * Possibili tre valori per type: 't','p','d'
 * Il flag full stampa triangolare o completa. LA VERSIONE FULL E' DEPRECATA,
 * non è ancora rimossa per testing futuri che pat ha in mente. in ogni caso who cares
 * il format del printato è sempre 3 cifre, 0 delle cui a dx della virgola.
 *
 * \pre
 *  \li f non NULLO
 *  \li aperto in scrittura
 *  \li pg non NULLO
 *  \li type vale o 't' o 'd' o 'p'
 *
 * @param f il su cui stampare il grafo
 * @param pg il grafo sa stampare
 * @param type indica quale delle matrici stampare. E' possibile scegliere tra uno di questi valori:
 * \li 't': stampa la matrice dei tempi
 * \li 'd': stampa la matrice delle domande
 * \li 'p': stampa la matrice dei profitti
 */
void printAdjMatrix(FILE* f, Pgraph pg, char type);

//FORPAT aggiornare la documentazione
/* stampa a stdout della matrice di adiacenza completa
 * * Il flag full stampa triangolare o completa.
 * il format del printato è sempre 3 cifre, 0 delle cui a dx della virgola. */
void printWholeMatrix(FILE* f, Pgraph pg);

/** stampa sul file la rappresentazione dell'arco e la rappresentazione delle caratteristiche fisiche dell'arco
 * (tempo, domanda e profitto)
 *
 * \pre
 *  \li f aperto in scrittura
 *  \li pg non NULL
 *  \li pg contiene l'EdgeGraph dell'ID arco edge
 *  \li edge non NULL
 *
 * \post
 *  \li f viene modificato aggiungendo caratteri
 *
 * @param f il file da modificare
 * @param pg il grafo in cui risiede l'EdgeGraph indicato da edge
 * @param edge l'edge da stampare
 */
void printEdgeFromGraph(FILE* f, Pgraph pg, PedgeID edge);

/** ritorna un arco del grafo. Dato che l'algoritmo funziona solo con la meta' superiore del grafo questo metodo
 * capisce immediatamente se l'arco passato si trova nella matrice triangola inferiore del grafo oppure no. Se e' cosi'
 * la funzione provvede a ribaltare gli indici. In questo modo la funzione ritorna il riisultato corretto in questi entrambi casi:
 * \li getEdge(&graph,initEdgeID(3,10));
 * \li getEdge(&graph,initEdgeID(10,3));
 * E' dunque altamente <b>consigliato</b> utilizzare questa funzione per gestire il grafo! Una altrettanto valida alternativa
 * all'uso di questa funzione e' la funzione validateEdgeID(); l'uso di validateEdgeID() e' consigliato qualora si voglia
 * accedere piu' e piu' volte allo stesso arco (incrementando quindi l'efficienza); l'uso invece di questa funzione invece
 * e' consigliato quando l'arco deve essere ottenuto una volta soltanto
 *
 * \pre
 * 	\li pg non deve essere NULL;
 * 	\li pg deve essere stato inizializzato con le funzioni presenti in questo header;
 * 	\li edge non deve essere NULL;
 * 	\li la cooridnata i di edge e quella j devono essere diverse, maggiori di zero e minori di __N__;
 * \post
 * 	\li un riferimento non NULL ad un arco contenente le sue caratteristiche fisiche.
 *
 * @param pg il grafo da considerare
 * @param edge le coordinate dell'arco
 * @return l'edge contenuto nella matrice di adiacenza alle coordinate row=i e column=j
 */
PedgeGraph getEdgeGraph(Pgraph pg, PedgeID edge);

/** ritorna una valutazione statica dell' arco del grafo dal punto di vista temporale . Dato che l'algoritmo funziona solo con la meta' superiore del grafo questo metodo
 * capisce immediatamente se l'arco passato si trova nella matrice triangola inferiore del grafo oppure no. Se e' cosi'
 * la funzione provvede a ribaltare gli indici. In questo modo la funzione ritorna il riisultato corretto in questi entrambi casi:
 * \li getEdge(&graph,initEdgeID(3,10));
 * \li getEdge(&graph,initEdgeID(10,3));
 * E' dunque altamente <b>consigliato</b> utilizzare questa funzione per gestire il grafo!
 *
 * \pre
 * 	\li pg non deve essere NULL;
 * 	\li pg deve essere stato inizializzato con le funzioni presenti in questo header;
 * 	\li edge non deve essere NULL;
 * 	\li la cooridnata i di edge e quella j devono essere diverse, maggiori di zero e minori di __N__;
 * 	\li la funzione initSmartWeights() deve essere gia' stata richiamata
 * \post
 * 	\li il peso dell'arco cercato
 *
 * @param pg il grafo da considerare
 * @param edge le coordinate dell'arco
 * @return il peso dell'edge dal punto di vista temporale contenuto nella matrice di adiacenza alle coordinate row=i e column=j
 */
Weight getEdgeTimeWeight(Pgraph pg,PedgeID edge);

/** ritorna una valutazione statica dell' arco del grafo dal punto di vista della capacita'. Dato che l'algoritmo funziona solo con la meta' superiore del grafo questo metodo
 * capisce immediatamente se l'arco passato si trova nella matrice triangola inferiore del grafo oppure no. Se e' cosi'
 * la funzione provvede a ribaltare gli indici. In questo modo la funzione ritorna il riisultato corretto in questi entrambi casi:
 * \li getEdge(&graph,initEdgeID(3,10));
 * \li getEdge(&graph,initEdgeID(10,3));
 * E' dunque altamente <b>consigliato</b> utilizzare questa funzione per gestire il grafo!
 *
 * \pre
 * 	\li pg non deve essere NULL;
 * 	\li pg deve essere stato inizializzato con le funzioni presenti in questo header;
 * 	\li edge non deve essere NULL;
 * 	\li la cooridnata i di edge e quella j devono essere diverse, maggiori di zero e minori di __N__;
 * 	\li la funzione initSmartWeights() deve essere gia' stata richiamata
 * \post
 * 	\li il peso dell'arco cercato
 *
 * @param pg il grafo da considerare
 * @param edge le coordinate dell'arco
 * @return il peso dell'edge dal punto di vista della capacita' contenuto nella matrice di adiacenza alle coordinate row=i e column=j
 */
Weight getEdgeLoadWeight(Pgraph pg, PedgeID edge);

/*
 * metodo per modularizzazione low-level del codice. Dato un arco (i,j) ne estrae
 * il dato del type desiderato tra (t,d,p).
 */
int getEdgeGraphValue(Pgraph pg, int i, int j, char type);

/**
 * @return vero sse profitto e domanda dell'arco ij sono non nulli.
 */
bool isEdgeProfitable(Pgraph pg, PedgeID pid);

/** Criterio valutatore del peso (positivo) di un arco, come descritto in documentazione.
 *  \pre
 *   \li pg deve essere gia' stato inizializzato;
 *   \li edge deve essere gia' stato inizializzato;
 *   \li la funzione initSmartWeights() deve essere gia' stata richiamata
 *
 *  \post
 *   \li un numero compreso tra 0 e +infinito oppure EVAL_FAILED in caso l'utente abbia inserito un valore non accettabile mode
 *   \li una valutazione statica dell'arco (non coinvolgente dunque lo scenario in cui l'arco sia gia' stato raccolto d altri camion)
 *
 *  @param pg indica il grafo che contiene l'arco da valutare
 *  @param edge indica le coordinate dell'arco
 *  @param tau indica il tempo che un particolare veicolo ha gia' consumato viaggiando in altri archi
 *  @param theta indica la capacita' che un particolare veicolo ha gia' consumato viaggiando in altri archi
 *  @param mode indica come far funzionare il valutatore; ci sono 3 modalita':
 *   \li mode == EVAL_PURE => modo pure: viene restituito solo il profitto;
 *   \li mode == EVAL_SMART => modo smart: viene pesato il profitto sui due costi;
 *   \li mode == EVAL_WISE => modo wise: viene pesato il profitto sui costi e sulle risorse;
 *  @return un numero compreso tra 0 e infinito oppure EVAL_FAILED in caso l'utente abbia inserito un valore non accettabile mode
 *
 */
Weight evaluateEdge(Pgraph pg, PedgeID pedge, int tau, int theta, short mode);

/** Valuta un arco che già si sa non essere interessante dal punto di vista della raccolta/servizio (perchè non profittabile o già servito da altri).
 * Nella fattispecie, se
 * 	\li l'arco non è intrinsecamente profittabile, restituisce zero.
 * 	\li l'arco è profittabl
 * 		 valutazioni maggiori corrispondono ad archi migliori.
 */
Weight evaluateNonProfitableEdge(Pgraph pg, PedgeID pedge, int tau, short mode);


/** imposta dei nuovi valori al particolare arco. L'id puo' assumere qualunque valore non nullo
 *
 *  \pre
 *   \li pg non e' NULL
 *   \li id non e' NULL
 *  \post
 *   \li nel grafo pg all'interno della cella puntata da id si trovano i 3 valori t, d e p passati come parametro
 *
 * @param pg il grafo da modificare
 * @param id l'arco da modificare
 * @param t il tempo ipiegato per percorrerlo
 * @param d la quantita' da diminuire al camion qualora l'arco venga soddisfatto
 * @param p il profitto ricavabile qualora l'arco venga soddisfatto
 */
void setEdgeGraph(Pgraph pg,PedgeID id, int t,int d,int p);

/**aggiorna il costo temporale per attraversare questo arco
 *
 * \pre
 *  \li pg non NULL
 *  \li id non NULL
 *  \li t positivo non nullo
 *  \li id e' stato validato in modo che i<j
 * \post
 *  l'arco indicato da id ha un costo temporale di attraversamento pari a t
 *
 * @param pg il grafo da modificare
 * @param id l'arco da modificare
 * @param t il nuovo costo temporale dell'arco
 */
void setEdgeGraphTime(Pgraph pg,PedgeID id,int t);

/** imposta dei nuovi valori al particolare arco. La coppia (i;j) puo' assumere qualunque valore non nullo
 *
 *  \pre
 *   \li pg non e' NULL
 *  \post
 *   nel grafo pg all'interno della cella puntata da (i;j) si trovano i 3 valori t, d e p passati come parametro
 *
 * @param pg il grafo da modificare
 * @param id l'arco da modificare
 * @param t il tempo ipiegato per percorrerlo
 * @param d la quantita' da diminuire al camion qualora l'arco venga soddisfatto
 * @param p il profitto ricavabile qualora l'arco venga soddisfatto
 */
void setEdgeGraphByIndexes(Pgraph pg, int i, int j, int t, int d, int p);

// Itera l'algoritmo di Dijkstra su ogni nodo, per riempire la tabella
void dijkstraFiller(Pgraph pg);

/** dato il grafo passato la funzione si preoccupa di correggere eventuali triangoli all'interno del grafo
 * che non soddisfano la disuguaglianza triangolare. Per esempio se all'interno del grafo esistessero 3 lati formanti
 * un triangolo rispettivamente di lati 1,2,e 10 il correttore si preoccuperebbe di modificare il 10 in valore più consono
 * (in questo caso 3). Generalemente se dato un triangolo qualsiasi all'interno del grafo tale triangolo non soddisfa la condizione:
 * \f[
 *	c \leq a+b
 * \f]
 * allora l'ipotenusa viene forzata al valore \f a+b \f
 *
 * \pre
 *  \li il grafo e' meglio che non sia completo
 *  \li lati non esistenti nella matrice di adiancenza hanno un valore maggiore o uguale a INFINITE
 * \post
 *  \li il grafo non presenta disuguaglianze triangolari non soddisfatte
 *
 * @param graph il grafo da modificare
 */
void graphCorrector(Pgraph pg);

/** Stampa T-tau e Q-theta. */
void printRemainingResources(FILE* f, Pgraph pg, int tau, int theta);

#endif /* GRAPH_H_ */
